﻿using ApplicationBase.Core.Cache;
using IdentityService.Domain.Aggregates.PermitAggregate;
using IdentityService.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;

namespace IdentityService.Application.QueryService
{
    public interface IPermitQueryService
    {
        Task<PermitRoleSet> GetPermitRoleSet(string permitName);
        Task<Permit?> GetPermitTree();
    }

    public class PermitQueryService : IPermitQueryService
    {
        private readonly AppIdentityDbContext _dbContext;
        private readonly IMemoryCache _cache;
        private readonly ICacheService _cacheService;
        public PermitQueryService(AppIdentityDbContext dbContext, IMemoryCache cache, ICacheService cacheService)
            => (_dbContext, _cache, _cacheService) = (dbContext, cache, cacheService);

        public async Task<PermitRoleSet> GetPermitRoleSet(string permitName)
        {
            var sets = _cache.Get<Dictionary<string, PermitRoleSet>>("PermitRoleSets");
            if(sets == null)
            {
                var dict = await GeneratePermitRoleSets();
                _cache.Set("PermitRoleSets", dict, TimeSpan.FromHours(1));
                return dict[permitName];
            }
            else
            {
                return sets[permitName];
            }
        }

        public async Task<Permit?> GetPermitTree()
        {
            var result = await _cacheService.GetOrCreateAsync("PermitTree", async cacheEntry =>
            {
                cacheEntry.SlidingExpiration = TimeSpan.FromMinutes(5);
                var permits = await _dbContext.Permit.AsNoTracking()
                    .Where(it => it.ParentId != null || it.Name == PermitDictionary.SuperAdmin).ToListAsync();
                var superAdmin = permits[0];
                for (int i = 1; i < permits.Count; i++)
                {
                    superAdmin.AddDescendant(permits[i]);
                }
                return superAdmin;
            });

            return result;
        }

        private async Task<Dictionary<string, PermitRoleSet>> GeneratePermitRoleSets()
        {
            var dict = new Dictionary<string, PermitRoleSet>();

            var roles = await _dbContext.Role.AsNoTracking().Include(it => it.Permits)
                .Where(it => it.AppName == App.Name).ToListAsync();
            var permitTree = await GetPermitTree();

            foreach(var role in roles)
            {
                foreach(var rolePermit in role.Permits)
                {
                    var permit = permitTree!.FindDescendantById(rolePermit.PermitId);
                    if(permit != null)
                    {
                        var permitNames = permit.GetAllDescendantName();
                        foreach(var permitName in permitNames)
                        {
                            if(dict.ContainsKey(permitName))
                            {
                                dict[permitName].Roles.Add(role.Name);
                            }
                            else
                            {
                                var set = new PermitRoleSet(permitName);
                                set.Roles.Add(role.Name);
                                dict.Add(permitName, set);
                            }
                        }
                    }
                }
            }

            return dict;
        }
    }

    public class PermitRoleSet
    {
        public string PermitName { get; set; }
        public HashSet<string> Roles { get; set; } = new();

        public PermitRoleSet(string name) => PermitName = name;
    }
}
